using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.IO;

namespace bkfviewer {
    public partial class VForm : Form {
        public VForm() {
            InitializeComponent();
        }

        FileStream fs = null;

        const int MinBlkSize = 1024;

        int alignBytes = MinBlkSize;

        List<Int64> aloffSfmb = new List<Int64>();

        void Walk(Stream si) {
            while (true) {
                Int64 off = fs.Position;

                bool hasStream = true;

                si.Flush();
                si.Position = off;
                byte[] binHdr = BSt.ReadBytes(si, MinBlkSize);
                MTF_DB_HDR header = new MTF_DB_HDR(binHdr);
                if (header.DBLKName == "TAPE") {
                    MTF_TAPE1 tapeHeader = new MTF_TAPE1(binHdr);
                    AddItem(header.DBLKName, off, 0, tapeHeader);
                    alignBytes = tapeHeader.FormatLogicalBlockSize;

                    if (0 == (si.Length & (alignBytes - 1))) {
                        si.Flush();
                        si.Position = si.Length - alignBytes;

                        byte[] sfmbHdr = BSt.ReadBytes(si, MinBlkSize);
                        MTF_SFMB1 myHdr = new MTF_SFMB1(sfmbHdr);

                        if (myHdr.DBLKName == "SFMB") {
                            foreach (Int64 newoff in myHdr.PBAofPreviousFilemarksArray) {
                                aloffSfmb.Add(newoff * alignBytes);
                            }

                            aloffSfmb.Sort();
                        }
                    }
                }
                else if (header.DBLKName == "SFMB") {
                    MTF_SFMB1 myHdr = new MTF_SFMB1(binHdr);
                    AddItem(header.DBLKName, off, 0, myHdr);
                    hasStream = false;
                    while (aloffSfmb.Remove(off)) { }
                }
                else if (header.DBLKName == "SSET") {
                    MTF_SSET1 myHdr = new MTF_SSET1(binHdr);
                    AddItem(header.DBLKName, off, 0, myHdr);
                }
                else if (header.DBLKName == "VOLB") {
                    MTF_VOLB1 myHdr = new MTF_VOLB1(binHdr);
                    AddItem(header.DBLKName, off, 0, myHdr);
                }
                else if (header.DBLKName == "DIRB") {
                    MTF_DIRB1 myHdr = new MTF_DIRB1(binHdr);
                    AddItem(header.DBLKName, off, 0, myHdr);
                }
                else if (header.DBLKName == "FILE") {
                    MTF_FILE1 myHdr = new MTF_FILE1(binHdr);
                    AddItem(header.DBLKName, off, 0, myHdr);
                }
                else if (header.DBLKName == "EOTM") {
                    MTF_EOTM1 myHdr = new MTF_EOTM1(binHdr);
                    AddItem(header.DBLKName, off, 0, myHdr);
                }
                else if (header.DBLKName == "ESET") {
                    MTF_ESET1 myHdr = new MTF_ESET1(binHdr);
                    AddItem(header.DBLKName, off, 0, myHdr);
                }
                else {
                    AddItem(header.DBLKName, off, 0, header);
                }

                si.Position = off = (off + header.OffsetToFirstEvent);

                if (hasStream) {
                    while (true) {
                        si.Position = off = RUt.RoundUpTo4(off);

                        MTF_STREAM_HDR streamHeader = new MTF_STREAM_HDR(BSt.ReadBytes(si, 22));

                        AddItem(streamHeader.StreamID4CC, off, 1, streamHeader);

                        off = si.Position + Convert.ToInt64(streamHeader.StreamLength);

                        if (streamHeader.StreamID4CC == "SPAD") {
                            break;
                        }
                    }

                    si.Position = RUt.RoundUpTo4(off);
                }
            }
        }

        private void bOpenbkf_Click(object sender, EventArgs e) {
            if (ofdbkf.ShowDialog(this) == DialogResult.OK) {
                using (WIP wip = WIP.Show(this)) {
                    if (fs != null) {
                        fs.Dispose();
                        fs = null;
                    }
                    fs = File.OpenRead(llbkf.Text = ofdbkf.FileName);

                    hv.Contents = fs;
                    hv.Position = 0;

                    lvE.Items.Clear();
                    ii = -1;

                    lvE.BeginUpdate();

                    try {
                        Stream si = fs;

                        try {
                            Walk(si);
                        }
                        catch (EndOfStreamException) {

                        }

                        try {
                            for (int x = 0; x < aloffSfmb.Count; ) {
                                Int64 off = si.Position = aloffSfmb[x]; while (aloffSfmb.Remove(off)) { }
                                Walk(si);
                            }
                        }
                        catch (EndOfStreamException) {

                        }
                    }
                    finally {
                        lvE.EndUpdate();
                    }
                }
            }
        }

        class BSt {
            public static byte[] ReadBytes(Stream si, int cb) {
                byte[] bin = new byte[cb];
                for (int x = 0; x < cb; ) {
                    int r = si.Read(bin, x, cb - x);
                    if (r < 1) throw new EndOfStreamException();
                    x += r;
                }
                return bin;
            }
        }

        class RUt {
            public static Int64 RoundUpTo4(Int64 val) {
                return (val + 3) & (~3);
            }
        }

        int ii = -1;

        private void RevAddItem(String name, Int64 off, int indent, object header) {
            ListViewItem lvi = new ListViewItem(new string(' ', 3 * indent) + name);
            lvi.SubItems.Add(off.ToString("#,##0"));
            lvi.ImageKey = name;
            lvi.Tag = header;

            if (ii < 0) {
                ii = lvE.Items.Add(lvi).Index;
            }
            else {
                lvE.Items.Insert(ii, lvi);
            }
        }

        void AddItem(String name, Int64 off, int indent, object header) {
            ListViewItem lvi = new ListViewItem(new string(' ', 3 * indent) + name);
            lvi.SubItems.Add(off.ToString("#,##0"));
            lvi.ImageKey = name;
            lvi.Tag = header;

            lvE.Items.Add(lvi);
        }

        private void lvE_SelectedIndexChanged(object sender, EventArgs e) {
            foreach (int i in lvE.SelectedIndices) {
                ListViewItem lvi = lvE.Items[i];
                // http://msdn.microsoft.com/ja-jp/library/aa302326.aspx
                props.SelectedObject = lvi.Tag;

                Int64 pos;
                if (Int64.TryParse(lvi.SubItems[1].Text.Replace(",", ""), out pos)) hv.Position = pos;
                break;
            }
        }
    }
}